/**\class MacHeader
 *
 *Unique Ethernet addresses, along with the total number of packets containing each address.
 *Unique Represent Ethernet addresses in hex-colon format.
 *
 */

#include "MacHeader.h"
#include "iu_wiretap.h"

map<string, MacHeader*> MacHeader::macHeaderMap_;
int MacHeader::nBroadcast=0;
MacHeader::MacHeader()
{

}
/**
 * Create Header from mac address string
 * \param address is a string of mac address like 00:08:74:ad:f1:9b
 */
MacHeader::MacHeader(string address)
{
  nRecvPacket_ = 0;
  nSendPacket_ = 0;
  //cout << "Create New Mac Header: " << address << endl;
  address_ = address;

}
/**
 *  Increase Mac Header Receive packet counter
 */
void
MacHeader::increaseRecvPacketCounter(int n)
{
  nRecvPacket_+=n;
}


/**
 *  Increase Mac Header Send packet counter
 */
void
MacHeader::increaseSendPacketCounter()
{
  nSendPacket_++;
}
/**
 * Convert raw mac address data to Mac address string
 *
 *\param start is a porinter to start byte
 *\return string like 00:0b:82:01:fc:42
 */
string
MacHeader::rawMacToString(const u_char * start)
{
  stringstream oss;// we use stream
  int val;

  for (int i = 0; i < 6; i++)
    {
      val = (int) start[i]; //We need to save that chat to int
      oss << hex << setw(2) << setfill('0') << val; //out put hex format string to oss
      if (i != 5)
        {
          oss << ":";
        }
    }
  return oss.str();
}

MacHeader::~MacHeader()
{
  // TODO Auto-generated destructor stub
}

MacHeader::MacHeader(const MacHeader &Header)
{
}
/**
 * Print out Ethernet address info like Mac address:00:08:74:ad:f1:9b  Received 0 packets. Send 2 packets.
 */
string
MacHeader::printRecord()
{
  stringstream ss;
  ss << "MAC address: " << address_ << "  Received: " << setw(4) << left << nRecvPacket_
      << " Sent: " << setw(4) << left << nSendPacket_ << " Total: " << setw(4) << left
      << nRecvPacket_ + nSendPacket_ << " packets" << endl;
  return ss.str();
}

/**
 * Print out all Ethernet address info
 */
string
MacHeader::showAll()
{
  stringstream ss;
  cout<< "MAC Header Info----------------------------------------------------------------" << endl;
  cout << macHeaderMap_.size() << " mac addresses in this file:" << endl;
  //Here is how to get iterator
  //the type of 'it' is map<string,MacHeader*>::const_iterator
  //We start loop from begin() to end()
  map<string, MacHeader*>::const_iterator it = macHeaderMap_.begin();
  for (; it != macHeaderMap_.end(); it++)
    {
      //cout << "key  " << it->first; //first is the key string
      ss << it->second->printRecord(); //it->second is the value of map,which is a MacHeader*
    }
  return ss.str();
}
/**
 *  Handle Ethernet header
 *
 *  Record new mac address in a map, and add send & recv counter for each address accordingly
 *  /param packet is a pointer to the whole packet
 *  /param index is a global index, indicate the start point of that header
 *  /return next Protocol type
 */
Protocol
MacHeader::handlePacket(const u_char * packet, int& index)
{

  string source;
  string dest;
  Protocol p=PROTOCOL_NULL;
  //Cast packet into Ethernet packet
  struct SniffEthernet *header = (struct SniffEthernet *) (packet);
  //Get destination and source string from packet
  dest = rawMacToString(packet);
  source = rawMacToString(packet + 6);

  if(dflag!=0) {
    cout << "MAC Source address:      " << source << endl;
    cout << "MAC Destination address: " << dest << endl;
  }

  //if address not in map, create one, else increase counter
  if (source !="ff:ff:ff:ff:ff:ff") //source never eq to broadcast address
    {
      if (MacHeader::macHeaderMap_.count(source) <= 0) //check if source in map
        {
          MacHeader::macHeaderMap_[source] = new MacHeader(source);
          MacHeader::macHeaderMap_[source]->increaseRecvPacketCounter(nBroadcast);//add previous broadcast
          MacHeader::macHeaderMap_[source]->increaseSendPacketCounter();
        }
      else
        {
          MacHeader::macHeaderMap_[source]->increaseSendPacketCounter();
        }
    }
  //Handle destination address
  if (dest == "ff:ff:ff:ff:ff:ff")// Boardcast.Increase all address receive counter.
    {
      MacHeader::nBroadcast++;//Remember the number of broadcast, so next time when we find new mac address, we could add it to receive counter.
      map<string, MacHeader*>::const_iterator it = macHeaderMap_.begin();
      for (; it != macHeaderMap_.end(); it++)
        {
          if(it->first!=source){ //sender can not receive broadcast
          it->second->increaseRecvPacketCounter(1); //it->second is the value of map,which is a MacHeader*
          }
        }
    }
  else
    {
      if (MacHeader::macHeaderMap_.count(dest) <= 0) //check if dest already in map
        {
          MacHeader::macHeaderMap_[dest] = new MacHeader(dest);
          MacHeader::macHeaderMap_[dest]->increaseRecvPacketCounter(nBroadcast+1);
        }
      else
        {
          MacHeader::macHeaderMap_[dest]->increaseRecvPacketCounter(1);
        }
    }
  //Which Protocol next?

  if (ntohs(header->etherType) == IP_INSIDE)
    {
      //cout<<"next is ip"<<endl;
      p = PROTOCOL_IP;
    }
  else if (ntohs(header->etherType) == ARP_INSIDE)
    {
      //cout<<"next is arp"<<endl;
      p = PROTOCOL_ARP;
    }
  else
    {
      p = PROTOCOL_NULL;
    }
  //Increase index;
  index+=SIZE_ETHERNET;
  return p;
}
